#include <tvm/tvm.h>

#include <tvm/tvm_lexer.h>
#include <tvm/tvm_parser.h>

struct tvm_ctx *tvm_vm_create()
{
    // 根据结构体大小申请空间
    struct tvm_ctx *vm = (struct tvm_ctx *)calloc(1, sizeof(struct tvm_ctx));

    if (!vm)
        return NULL;

    // 根据宏定义的值申请变量存储的空间
    vm->mem = tvm_mem_create(MIN_MEMORY_SIZE);
    // 创建存放代码的地址空间
    vm->prog = tvm_prog_create();

    if (!vm->mem || !vm->prog)
    {
        tvm_vm_destroy(vm);
        return NULL;
    }

    // 创建栈空间
    tvm_stack_create(vm->mem, MIN_STACK_SIZE);
    return vm;
}

// 虚拟机解释器
int tvm_vm_interpret(struct tvm_ctx *vm, char *filename)
{
    FILE *filp = NULL;
    int source_length = 0;

    /* Attempt to open the file.
	 * If the file cannot be opened, try once more.
	 */
    if (filename)
    {
        for (int i = 0; i < 2; i++)
        {
            if (!filp)
                filp = tvm_fopen(filename, ".vm", "r");
        }
    }

    if (!filp)
    {
        printf("File was not found, or does not exist. Unable to interpret.\n");
        return 1;
    }

    source_length = tvm_flength(filp);
    // 根据文件的文本长度创建相应大小的地址空间
    char *source = calloc(source_length, sizeof(char));

    // 将文本内容拷贝到申请得到空间地址上
    tvm_fcopy(source, source_length, filp);
    fclose(filp);

    // 对define进行预处理
    // &source， 取值
    int err = tvm_preprocess(&source, &source_length, vm->prog->defines);

    /* The preprocessor encountered a problem. */
    if (err < 0)
        return 1;

    struct tvm_lexer_ctx *lexer_ctx = lexer_create();

    // 语法解析
    tvm_lex(lexer_ctx, source, vm->prog->defines);
    // 释放存放代码的空间地址
    free(source);

    // 标签值解析
    if (tvm_parse_labels(vm, (const char ***)lexer_ctx->tokens) != 0)
        return 1;

    // 将指令解析为字节码
    if (tvm_parse_program(vm, (const char ***)lexer_ctx->tokens) != 0)
        return 1;

    tvm_lexer_destroy(lexer_ctx);
    return 0;
}

void tvm_vm_run(struct tvm_ctx *vm)
{
    int *instr_idx = &vm->mem->registers[0x8].i32;
    *instr_idx = vm->prog->start;

    for (; vm->prog->instr[*instr_idx] != -0x1; ++(*instr_idx))
        tvm_step(vm, instr_idx);
}

void tvm_vm_destroy(struct tvm_ctx *vm)
{
    if (vm && vm->mem)
        tvm_mem_destroy(vm->mem);
    if (vm && vm->prog)
        tvm_prog_destroy(vm->prog);
    if (vm)
        free(vm);
}
